fix: use pull_request_target for agentic CI on fork PRs#541
fix: use pull_request_target for agentic CI on fork PRs#541andreatgretel merged 4 commits intomainfrom
Conversation
Greptile SummaryThis PR switches the agentic CI review workflow from
|
| Filename | Overview |
|---|---|
| .github/workflows/agentic-ci-pr-review.yml | Switches to pull_request_target with expression-injection hardening and base-branch recipe checkout; one residual prompt-injection vector remains via fork-controlled CLAUDE.md/AGENTS.md loaded automatically by the claude CLI. |
Sequence Diagram
sequenceDiagram
participant Fork as Fork PR / Maintainer
participant GH as GitHub Actions
participant Gate as gate job (ubuntu-latest)
participant Env as agentic-ci environment
participant Review as review job (self-hosted)
participant Claude as claude CLI
Fork->>GH: pull_request_target event (opened/labeled/ready)
GH->>Gate: start gate job (no secrets)
Gate->>Gate: check collaborator permission via GH API
alt no write access
Gate-->>GH: allowed=false stop
end
Gate-->>GH: allowed=true
GH->>Env: request environment approval
Env-->>GH: approved
GH->>Review: start review job (secrets available)
Review->>Review: checkout fork HEAD to GITHUB_WORKSPACE includes CLAUDE.md
Review->>Review: sparse-checkout base branch .agents/recipes to base-recipes/
Review->>Claude: claude -p prompt from base-recipes/
Note over Claude: loads CLAUDE.md from GITHUB_WORKSPACE
Claude->>Review: review output
Review->>GH: post PR comment
Comments Outside Diff (1)
-
.github/workflows/agentic-ci-pr-review.yml, line 139-143 (link)Residual prompt injection via CLAUDE.md / AGENTS.md
The
Checkout PR branchstep checks out the fork's full tree (no sparse-checkout) to$GITHUB_WORKSPACE/, which is also the working directory whenclaudeis invoked. The Claude Code CLI automatically loadsCLAUDE.md(and any files it@-includes, such asAGENTS.md) from the working directory as high-priority project instructions — even in non-interactive-pmode. A fork PR that modifiesCLAUDE.mdorAGENTS.mdcan therefore inject arbitrary instructions into the agent whileANTHROPIC_API_KEYis in scope, circumventing the recipe protection added in this PR.The fix is to also add a sparse-checkout (or
--exclude) on the first checkout, or to runclaudewith--cwdpointing to a directory that does not contain fork-controlled instruction files. For example:- name: Checkout PR branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ steps.head.outputs.sha }} fetch-depth: 0 sparse-checkout-cone-mode: false sparse-checkout: | /* !CLAUDE.md !AGENTS.md !.claude
Or alternatively, delete/overwrite these files from the checkout before invoking
claude, replacing them with known-good copies from the base branch.Prompt To Fix With AI
This is a comment left during a code review. Path: .github/workflows/agentic-ci-pr-review.yml Line: 139-143 Comment: **Residual prompt injection via CLAUDE.md / AGENTS.md** The `Checkout PR branch` step checks out the fork's full tree (no sparse-checkout) to `$GITHUB_WORKSPACE/`, which is also the working directory when `claude` is invoked. The Claude Code CLI automatically loads `CLAUDE.md` (and any files it `@`-includes, such as `AGENTS.md`) from the working directory as high-priority project instructions — even in non-interactive `-p` mode. A fork PR that modifies `CLAUDE.md` or `AGENTS.md` can therefore inject arbitrary instructions into the agent while `ANTHROPIC_API_KEY` is in scope, circumventing the recipe protection added in this PR. The fix is to also add a sparse-checkout (or `--exclude`) on the first checkout, or to run `claude` with `--cwd` pointing to a directory that does not contain fork-controlled instruction files. For example: ```yaml - name: Checkout PR branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ steps.head.outputs.sha }} fetch-depth: 0 sparse-checkout-cone-mode: false sparse-checkout: | /* !CLAUDE.md !AGENTS.md !.claude ``` Or alternatively, delete/overwrite these files from the checkout before invoking `claude`, replacing them with known-good copies from the base branch. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: .github/workflows/agentic-ci-pr-review.yml
Line: 139-143
Comment:
**Residual prompt injection via CLAUDE.md / AGENTS.md**
The `Checkout PR branch` step checks out the fork's full tree (no sparse-checkout) to `$GITHUB_WORKSPACE/`, which is also the working directory when `claude` is invoked. The Claude Code CLI automatically loads `CLAUDE.md` (and any files it `@`-includes, such as `AGENTS.md`) from the working directory as high-priority project instructions — even in non-interactive `-p` mode. A fork PR that modifies `CLAUDE.md` or `AGENTS.md` can therefore inject arbitrary instructions into the agent while `ANTHROPIC_API_KEY` is in scope, circumventing the recipe protection added in this PR.
The fix is to also add a sparse-checkout (or `--exclude`) on the first checkout, or to run `claude` with `--cwd` pointing to a directory that does not contain fork-controlled instruction files. For example:
```yaml
- name: Checkout PR branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ steps.head.outputs.sha }}
fetch-depth: 0
sparse-checkout-cone-mode: false
sparse-checkout: |
/*
!CLAUDE.md
!AGENTS.md
!.claude
```
Or alternatively, delete/overwrite these files from the checkout before invoking `claude`, replacing them with known-good copies from the base branch.
How can I resolve this? If you propose a fix, please make it concise.Reviews (4): Last reviewed commit: "fix: move expression interpolations to e..." | Re-trigger Greptile
Code Review: PR #541 — fix: use
|
|
@andreatgretel – seems like this one is worth checking:
This is the single most important thing. The PR adds environment: agentic-ci (line 90 in the new file) as the human-approval gate If the agentic-ci environment doesn't exist in GitHub repo settings with "required reviewers" configured:
Action: Confirm in GitHub repo settings that the agentic-ci environment exists with required reviewers before merging. Both the Greptile bot and the agentic CI review flagged this same point. |
Review:
|
|
confirmed - the on the other findings:
|
Recipe files define the agent's prompt. When using pull_request_target, the fork's HEAD is checked out, so a malicious fork could craft recipe files to exfiltrate API secrets via prompt injection. Fix by adding a second sparse checkout from the base branch for .agents/recipes/ and reading prompts from there instead of the fork tree.
Match the base-branch recipe checkout to v6.0.2 (same SHA as the PR branch checkout) for consistency.
Replace direct ${{ }} interpolation in run: blocks with env vars.
Most values are GitHub-controlled, but github.event.label.name can
contain arbitrary characters and could break shell quoting. Moving
everything to env: is consistent with the injection-hardening pattern
applied in the rest of the workflow.
9bc954a to
65aab94
Compare
Summary
The agentic CI review workflow doesn't work on fork PRs. Discovered on #526: the
pull_requesttrigger requires manual approval in the Actions tab (not on the PR) for each fork workflow run, and fork PR runs don't have access to repo secrets/variables so the job fails at theAGENTIC_CI_MODELcheck even after approval.Switching to
pull_request_targetfixes both since the workflow definition comes frommain, so GitHub skips the fork approval gate and base repo secrets/variables are available.Changes
Changed
pull_requesttopull_request_targetso fork PRs get secret access without per-run Actions tab approvalenvironment: agentic-cito thereviewjob for an explicit approval gate on the PR checks UIFixed
.agents/recipes/) are now checked out from the base branch intobase-recipes/, so fork PRs cannot tamper with the agent's prompt while API secrets are in scope (agentic-ci-pr-review.yml#L132-L140)${{ }}interpolations inrun:blocks moved toenv:blocks to eliminate shell injection surface from event payload valuesAttention Areas
agentic-ci-pr-review.yml- Only file changed. Security model: gate job checks collaborator permissions,agentic-cienvironment requires reviewer approval, recipe files come from base branch (not fork), no direct${{ }}interpolation in shellDescription updated with AI